use async_trait::async_trait;
use my_rust_core::{security::auth_request_validator::UserActiveTokenManager, shared::date_helper};
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter};
use std::sync::{Arc, LazyLock, Mutex};

use crate::entities;

// It only caches access tokens since refresh tokens never expire.
// By using cached access tokens would reduce access to database.
static CACHED_USER_ACTIVE_TOKENS: LazyLock<Arc<Mutex<Vec<entities::user_active_token::Model>>>> =
    LazyLock::new(|| Arc::new(Mutex::new(Vec::new())));

#[async_trait]
pub trait UserCachedActiveTokenManager: Send + Sync {
    async fn get_cached_user_active_tokens(
        &self,
    ) -> Arc<Mutex<Vec<entities::user_active_token::Model>>>;

    async fn reload_cached_user_active_tokens(&self);
}

pub struct UserActiveTokenManagerImpl {
    pub db: Arc<DatabaseConnection>,
}

impl UserActiveTokenManagerImpl {
    pub fn new(db: Arc<DatabaseConnection>) -> Self {
        UserActiveTokenManagerImpl { db }
    }
}

#[async_trait]
impl UserCachedActiveTokenManager for UserActiveTokenManagerImpl {
    async fn get_cached_user_active_tokens(
        &self,
    ) -> Arc<Mutex<Vec<entities::user_active_token::Model>>> {
        if CACHED_USER_ACTIVE_TOKENS.lock().unwrap().len() == 0 {
            self.reload_cached_user_active_tokens().await;
        }

        CACHED_USER_ACTIVE_TOKENS.clone()
    }

    /// Loads all enabled and valid access tokens.
    async fn reload_cached_user_active_tokens(&self) {
        let list = entities::prelude::UserActiveToken::find()
            .filter(
                Condition::all()
                    .add(
                        entities::user_active_token::Column::DateExpired
                            .gte(date_helper::get_current_utc_time()),
                    )
                    .add(entities::user_active_token::Column::Enabled.eq(true)),
            )
            .all(self.db.as_ref())
            .await;

        *CACHED_USER_ACTIVE_TOKENS.lock().unwrap() = list.unwrap();
    }
}

#[async_trait]
impl UserActiveTokenManager for UserActiveTokenManagerImpl {
    /// Checks if this access token is still active. If the data does not exist means the token has been revoked.
    async fn is_active_access_token(&self, username: &str, access_token: &str) -> bool {
        self.get_cached_user_active_tokens()
            .await
            .lock()
            .unwrap()
            .iter()
            .any(|token| token.username == username && token.access_token == access_token)
    }
}
